package com.serializetool;

/**
 * id = 1位空值+40位时间戳+10位工作机+13位序列号
 * 从2022年开始，最高43年，最多1024台主机，每毫秒最多8192个序列
 */
public class SnowflakeIdGenerator {


    public void init(long workerId) {
        if (workerId > MAX_WORKER_ID) {
            // workerId大于可以分配到的主机Id
            throw new IllegalArgumentException("woker Id is too big: " + workerId);
        }
        this.workerId = workerId;
    }

    public SnowflakeIdGenerator(long workerId) {
        init(workerId);
    }

    /**
     * 开始使用算法的时间: 2022-01-01 00:00:00
     */
    private static final long START_TIME = 1640966400000L;

    /**
     * worker id 的位数，最多支持1024个主机
     */
    private static final int WORKER_ID_BITS = 10;

    /**
     * SEQUENCE_BITS的位数，每秒主机最高可生成8192个序列号
     */
    private final static int SEQUENCE_BITS = 13;

    /**
     * worker id的二进制表示，1024
     * -1 的补码左移10位, 然后取反，获得10个1
     */
    private final static long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);

    /**
     * MAX_SEQUENCE的二进制表示，8102
     * -1 的补码左移10位, 然后取反，获得13个1
     */
    private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

    /**
     * worker 节点需要额外移动到序列号左边
     */
    private final static long APP_HOST_ID_SHIFT = SEQUENCE_BITS;

    /**
     * 时间戳需要移动woker和序列号左边
     */
    private final static long TIMESTAMP_LEFT_SHIFT = WORKER_ID_BITS + APP_HOST_ID_SHIFT;

    /**
     * 这个主机的workerId
     */
    private long workerId;

    /**
     * 上次生成ID的时间戳
     */
    private long lastTimestamp = -1L;

    /**
     * 当前毫秒下一个可以生成的序列号
     */
    private long sequence = 0L;

    /**
     * 生成唯一id的具体实现
     */
    public synchronized long getId() throws Exception{
        long current = System.currentTimeMillis();

        if (current < lastTimestamp) {
            // 如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过，出现问题返回-1
            return -1;
        }

        if (current == lastTimestamp) {
            // 如果当前生成id的时间还是上次的时间，那么对sequence序列号进行+1
            sequence++;

            if (sequence == MAX_SEQUENCE) {
                // 当前毫秒生成的序列数已经大于最大值，那么阻塞到下一个毫秒再获取新的时间戳
                current = this.nextMs(lastTimestamp);
            }
        } else {
            // 当前的时间戳已经是下一个毫秒
            sequence = 0L;
        }

        // 更新上次生成id的时间戳
        lastTimestamp = current;

        // 下面是正式生成代码
        long time = ((current - START_TIME) << TIMESTAMP_LEFT_SHIFT)>>>1;
        long workerId = this.workerId << APP_HOST_ID_SHIFT;
        return time | workerId | sequence;
    }

    /**
     * 阻塞到下一个毫秒
     */
    private long nextMs(long timeStamp) {
        long current = System.currentTimeMillis();
        while (current <= timeStamp) {
            current = System.currentTimeMillis();
        }
        return current;
    }


}
